#ifdef LIBN1_PLUGMODE
#include "libn1_plug.h"
#else
#include "libn1.h"
#endif

#include "both.h"

//TODO сделать общий интерфейс для libn и libn1, наследниками которых будут эти классы
//TODO написать класс libn, который будет реализовывать хранение и доступ к библиотеке по всем отрицаниям

Int8 n = 6, nPre = n - 1, fLen = 1 << n, fLenPre = 1 << nPre;
Int64 fCountPre = (Int64)1 << fLenPre;
LibN1 lib(nPre);
//LibN1 lib;

Int8 g6_2(Int64 f) {
  Int32 f3 = 0;
  Int32 f1x = f >> (fLen >> 1);
  Int32 f0x = f;
  cout << "f: " << hex << f << endl;
  cout << "f1x: " << hex << f1x << endl;
  cout << "f0x: " << hex << f0x << endl;
  Int8 fComp = 255;      
  do {
    Int8 tComp = lib.get(f3) + lib.get(f3 ^ f1x) + lib.get(f3 ^ f0x);
    if (tComp < fComp) fComp = tComp;
    f3++;
  } while (f3 != 0);
  return fComp; 
}

Int8 g6(Int64 f) {
  Int32 f3p = 1 << (1 << (nPre - 1));
  Int32 f3m = 1;
  Int32 f1x = f >> (fLen >> 1);
  Int32 f0x = f;
  size_t libi = 0;
  Int8 fCompRes = 255, fComp, f3Comp;
  Int32 f3_2;
  Int64 f3tof;
  for(Int64 f3 = 0; f3 < fCountPre; ) {
    f3tof = f3 + f3p;
    for(; f3 < f3tof; f3++, libi++) {
      f3Comp = lib.comp(libi);
      fComp = lib.get(f1x ^ f3) + lib.get(f0x ^ f3) + f3Comp;
      if (fComp < fCompRes) fCompRes = fComp;
      f3_2 = lib.fn1(f3);
      fComp = lib.get(f1x ^ f3_2) + lib.get(f0x ^ f3_2) + f3Comp;      
      if (fComp < fCompRes) fCompRes = fComp;      
    }
    f3 += f3m;
    f3p--; f3m++;      
  }
  return fCompRes;
}

#define EVO_COUNT_ITER 8  //количество популяций

struct EPItem {
  Int64 f;
  Int8 comp;
};

bool EPItem_cmp(EPItem a, EPItem b) {
  if (a.comp < b.comp) return true;
  if (a.comp == b.comp && a.f < b.f) return true;
  return false;
}

class EvoPopulation {
private:
  Int64 f, fx0, fx1;
public:
  Int64 MaxPopulation; //размер популяций после итерации
  vector<EPItem> a;
  
  void Push(Int64 f) {  
    EPItem temp; 
    temp.f = f; 
    temp.comp = lib.get(fx1 ^ f) + lib.get(fx0 ^ f) + lib.get(f);
    vector<EPItem>::iterator it = lower_bound(a.begin(), a.end(), temp, EPItem_cmp);
    if (a.size() != 0) if (it->f == temp.f) return ;
    a.insert(it, temp);  
  }

  EvoPopulation(Int64 f) {      
    MaxPopulation = 32;
    this->f = f;
    this->fx0 = f >> fLenPre;   
    this->fx1 = f & (((Int64)2 << 32) - 1);
/*    for(size_t i = 0; i < fLenPre; i++) {
      Push(((Int64)2 << i) - 1);
    }*/
/*    for(size_t i = 0; i < 16; i++) {
      Push((2 << i) - 1 + ((Int64)2 << 31) - ((Int64)2 << 30 - i));
      Push((((2 << i) - 1) << 16) + ((Int64)2 << 15) - ((Int64)2 << 14 - i));
    }*/
/*    for(size_t i = 0; i < fLenPre; i++) {
      Push(((Int64)2 << 31) - ((Int64)1 << i));
    }*/
    Int64 m = ((Int64)1 << 32) - 1;  
    #define MSIZE 31
    for(size_t i = 0; i < 32; i++) {
      Int64 t = (((Int64)1 << MSIZE) - 1) << i;
      t = (t&m) ^ ((t&~m)>>32);
      Push(t);
    }    
    
  }
  
  void Crossing(const Int64 f1, const Int64 f2) {    
    for(size_t i = 0; i < M.size(); i++) {
      Push((f1&M[i]) ^ (f2&~M[i]));
      Push((f1&~M[i]) ^ (f2&M[i]));      
    }
  }
  
  void Mutate(Int64 f) {
    size_t tof = fLenPre;
    for(size_t i = 0; i < tof; i++)
      Push(f^((Int64)1 << i));
  }
  
  void KillExtra() {
    a.resize(MaxPopulation);
  }
};

Int8 g6evo(Int64 f) {  
  EvoPopulation p(f);
  for(size_t CurrentIteration = 0; CurrentIteration < EVO_COUNT_ITER; CurrentIteration++) {
    vector<EPItem> aC = p.a;
//    cout << "Iteration: " << CurrentIteration+1 << endl;
    for(size_t i = 0; i < p.MaxPopulation; i++) 
      for(size_t j = i + 1; j < p.MaxPopulation; j++)
        p.Crossing(aC[i].f, aC[j].f);
    for(size_t i = 0; i < aC.size(); i++)
      p.Mutate(aC[i].f);
    p.KillExtra();
  }
  return p.a[0].comp;
}

